// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_PROXY_PROXY_CONFIG_H_
#define NET_PROXY_PROXY_CONFIG_H_

#include <string>

#include "net/base/net_export.h"
#include "net/proxy/proxy_bypass_rules.h"
#include "net/proxy/proxy_config_source.h"
#include "net/proxy/proxy_list.h"
#include "net/proxy/proxy_server.h"
#include "url/gurl.h"

namespace base {
class Value;
}

namespace net {

class ProxyInfo;

// ProxyConfig describes a user's proxy settings.
//
// There are two categories of proxy settings:
//   (1) Automatic (indicates the methods to obtain a PAC script)
//   (2) Manual (simple set of proxy servers per scheme, and bypass patterns)
//
// When both automatic and manual settings are specified, the Automatic ones
// take precedence over the manual ones.
//
// For more details see:
// http://www.chromium.org/developers/design-documents/proxy-settings-fallback
class NET_EXPORT ProxyConfig {
public:
    // ProxyRules describes the "manual" proxy settings.
    // TODO(eroman): Turn this into a class.
    // TODO(crbug.com/546383): Update the enum names; "TYPE_SINGLE_PROXY" really
    // means the same set of proxies are used for all requests.
    struct NET_EXPORT ProxyRules {
        enum Type {
            TYPE_NO_RULES,
            TYPE_SINGLE_PROXY,
            TYPE_PROXY_PER_SCHEME,
        };

        // Note that the default of TYPE_NO_RULES results in direct connections
        // being made when using this ProxyConfig.
        ProxyRules();
        ProxyRules(const ProxyRules& other);
        ~ProxyRules();

        bool empty() const
        {
            return type == TYPE_NO_RULES;
        }

        // Sets |result| with the proxies to use for |url| based on the current
        // rules.
        void Apply(const GURL& url, ProxyInfo* result) const;

        // Parses the rules from a string, indicating which proxies to use.
        //
        //   proxy-uri = [<proxy-scheme>"://"]<proxy-host>[":"<proxy-port>]
        //
        //   proxy-uri-list = <proxy-uri>[","<proxy-uri-list>]
        //
        //   url-scheme = "http" | "https" | "ftp" | "socks"
        //
        //   scheme-proxies = [<url-scheme>"="]<proxy-uri-list>
        //
        //   proxy-rules = scheme-proxies[";"<scheme-proxies>]
        //
        // Thus, the proxy-rules string should be a semicolon-separated list of
        // ordered proxies that apply to a particular URL scheme. Unless specified,
        // the proxy scheme for proxy-uris is assumed to be http.
        //
        // Some special cases:
        //  * If the scheme is omitted from the first proxy list, that list applies
        //    to all URL schemes and subsequent lists are ignored.
        //  * If a scheme is omitted from any proxy list after a list where a scheme
        //    has been provided, the list without a scheme is ignored.
        //  * If the url-scheme is set to 'socks', that sets a fallback list that
        //    to all otherwise unspecified url-schemes, however the default proxy-
        //    scheme for proxy urls in the 'socks' list is understood to be
        //    socks4:// if unspecified.
        //
        // For example:
        //   "http=foopy:80;ftp=foopy2"  -- use HTTP proxy "foopy:80" for http://
        //                                  URLs, and HTTP proxy "foopy2:80" for
        //                                  ftp:// URLs.
        //   "foopy:80"                  -- use HTTP proxy "foopy:80" for all URLs.
        //   "foopy:80,bar,direct://"    -- use HTTP proxy "foopy:80" for all URLs,
        //                                  failing over to "bar" if "foopy:80" is
        //                                  unavailable, and after that using no
        //                                  proxy.
        //   "socks4://foopy"            -- use SOCKS v4 proxy "foopy:1080" for all
        //                                  URLs.
        //   "http=foop,socks5://bar.com -- use HTTP proxy "foopy" for http URLs,
        //                                  and fail over to the SOCKS5 proxy
        //                                  "bar.com" if "foop" is unavailable.
        //   "http=foopy,direct://       -- use HTTP proxy "foopy" for http URLs,
        //                                  and use no proxy if "foopy" is
        //                                  unavailable.
        //   "http=foopy;socks=foopy2   --  use HTTP proxy "foopy" for http URLs,
        //                                  and use socks4://foopy2 for all other
        //                                  URLs.
        void ParseFromString(const std::string& proxy_rules);

        // Returns one of {&proxies_for_http, &proxies_for_https, &proxies_for_ftp,
        // &fallback_proxies}, or NULL if there is no proxy to use.
        // Should only call this if the type is TYPE_PROXY_PER_SCHEME.
        const ProxyList* MapUrlSchemeToProxyList(
            const std::string& url_scheme) const;

        // Returns true if |*this| describes the same configuration as |other|.
        bool Equals(const ProxyRules& other) const;

        // Exceptions for when not to use a proxy.
        ProxyBypassRules bypass_rules;

        // Reverse the meaning of |bypass_rules|.
        bool reverse_bypass;

        Type type;

        // Set if |type| is TYPE_SINGLE_PROXY.
        ProxyList single_proxies;

        // Set if |type| is TYPE_PROXY_PER_SCHEME.
        ProxyList proxies_for_http;
        ProxyList proxies_for_https;
        ProxyList proxies_for_ftp;

        // Used when a fallback has been defined and the url to be proxied doesn't
        // match any of the standard schemes.
        ProxyList fallback_proxies;

    private:
        // Returns one of {&proxies_for_http, &proxies_for_https, &proxies_for_ftp}
        // or NULL if it is a scheme that we don't have a mapping for. Should only
        // call this if the type is TYPE_PROXY_PER_SCHEME. Intentionally returns
        // NULL for "ws" and "wss" as those are handled specially by
        // GetProxyListForWebSocketScheme().
        ProxyList* MapUrlSchemeToProxyListNoFallback(const std::string& scheme);

        // Returns the first of {&fallback_proxies, &proxies_for_https,
        // &proxies_for_http} that is non-empty, or NULL.
        const ProxyList* GetProxyListForWebSocketScheme() const;
    };

    typedef int ID;

    // Indicates an invalid proxy config.
    static const ID kInvalidConfigID = 0;

    ProxyConfig();
    ProxyConfig(const ProxyConfig& config);
    ~ProxyConfig();
    ProxyConfig& operator=(const ProxyConfig& config);

    // Used to numerically identify this configuration.
    ID id() const { return id_; }
    void set_id(ID id) { id_ = id; }
    bool is_valid() const { return id_ != kInvalidConfigID; }

    // Returns true if the given config is equivalent to this config.  The
    // comparison ignores differences in |id()| and |source()|.
    bool Equals(const ProxyConfig& other) const;

    // Returns true if this config contains any "automatic" settings. See the
    // class description for what that means.
    bool HasAutomaticSettings() const;

    void ClearAutomaticSettings();

    // Creates a Value dump of this configuration.
    std::unique_ptr<base::DictionaryValue> ToValue() const;

    ProxyRules& proxy_rules()
    {
        return proxy_rules_;
    }

    const ProxyRules& proxy_rules() const
    {
        return proxy_rules_;
    }

    void set_pac_url(const GURL& url)
    {
        pac_url_ = url;
    }

    const GURL& pac_url() const
    {
        return pac_url_;
    }

    void set_pac_mandatory(bool enable_pac_mandatory)
    {
        pac_mandatory_ = enable_pac_mandatory;
    }

    bool pac_mandatory() const
    {
        return pac_mandatory_;
    }

    bool has_pac_url() const
    {
        return pac_url_.is_valid();
    }

    void set_auto_detect(bool enable_auto_detect)
    {
        auto_detect_ = enable_auto_detect;
    }

    bool auto_detect() const
    {
        return auto_detect_;
    }

    void set_source(ProxyConfigSource source)
    {
        source_ = source;
    }

    ProxyConfigSource source() const
    {
        return source_;
    }

    // Helpers to construct some common proxy configurations.

    static ProxyConfig CreateDirect()
    {
        return ProxyConfig();
    }

    static ProxyConfig CreateAutoDetect()
    {
        ProxyConfig config;
        config.set_auto_detect(true);
        return config;
    }

    static ProxyConfig CreateFromCustomPacURL(const GURL& pac_url)
    {
        ProxyConfig config;
        config.set_pac_url(pac_url);
        // By default fall back to direct connection in case PAC script fails.
        config.set_pac_mandatory(false);
        return config;
    }

private:
    // True if the proxy configuration should be auto-detected.
    bool auto_detect_;

    // If non-empty, indicates the URL of the proxy auto-config file to use.
    GURL pac_url_;

    // If true, blocks all traffic in case fetching the pac script from |pac_url_|
    // fails. Only valid if |pac_url_| is non-empty.
    bool pac_mandatory_;

    // Manual proxy settings.
    ProxyRules proxy_rules_;

    // Source of proxy settings.
    ProxyConfigSource source_;

    ID id_;
};

} // namespace net

#endif // NET_PROXY_PROXY_CONFIG_H_
